/*
 * Copyrignt (c) xuzeshui.com. All Rights Reserved.
 * Author: Zeshui Xu<i@xuzeshui.com>
 * Created Time: 2015-11-17 15:01:35
 * Last Modified: 2017-04-21 14:08:47
 * File Name: chinaregion/chinaregion.go
 * Description:
 */
package chinaregion

import (
	"strings"
	"time"

	"openapi/db"

	log "github.com/cihub/seelog"
)

var (
	chinaRegionService *ChinaRegionService

	cn2province   []*RegionItem            //省列表
	province2city map[string][]*RegionItem //省对应的城市列表
	city2county   map[string][]*RegionItem //城市对应的区县列表
	county2town   map[string][]*RegionItem //区县对应的镇
)

/*
	区域基本信息结构
*/
type RegionItem struct {
	Id   string `json:"region_id"`   //区域编码
	Name string `json:"region_name"` //区域名称
}

//regionCode解析后的region信息
type RegionInfo struct {
	ProvinceId string `json:"province_id"`
	Province   string `json:"province"`
	CityId     string `json:"city_id"`
	City       string `json:"city"`
	CountyId   string `json:"county_id"`
	County     string `json:"county"`
	TownId     string `json:"town_id"`
	Town       string `json:"town"`
}

func newRegionItem(id, name string) *RegionItem {
	return &RegionItem{
		Id:   id,
		Name: name,
	}
}

/*
	ChinaRegionService
*/
type ChinaRegionService struct{}

/*
	查询省列表
*/
func (p *ChinaRegionService) QueryChinaProvinces() []*RegionItem {
	if cn2province == nil {
		return make([]*RegionItem, 0, 0)
	}
	return cn2province
}

/*
	查询城市列表
*/
func (p *ChinaRegionService) QueryChinaCities(provinceId string) []*RegionItem {
	if province2city == nil {
		return make([]*RegionItem, 0, 0)
	}
	if data, ok := province2city[provinceId]; ok {
		return data
	}
	return make([]*RegionItem, 0, 0)
}

/*
	查询区县列表
*/
func (p *ChinaRegionService) QueryChinaCounties(cityId string) []*RegionItem {
	if city2county == nil {
		return make([]*RegionItem, 0, 0)
	}
	if data, ok := city2county[cityId]; ok {
		return data
	}
	return make([]*RegionItem, 0, 0)
}

/*
	查询镇列表
*/
func (p *ChinaRegionService) QueryChinaTowns(countyId string) []*RegionItem {
	if county2town == nil {
		return make([]*RegionItem, 0, 0)
	}
	if data, ok := county2town[countyId]; ok {
		return data
	}
	return make([]*RegionItem, 0, 0)
}

/*
	根据地址查询region_code
*/
func (p *ChinaRegionService) Adrr2RegionCode(province, city, county string) string {
	for _, pdt := range cn2province { //查询省列表
		if strings.Contains(pdt.Name, province) {
			for _, cdt := range province2city[pdt.Id] { //查询市列表
				if strings.Contains(cdt.Name, city) {
					for _, ccdt := range city2county[cdt.Id] { //查询区列表
						if strings.Contains(ccdt.Name, county) {
							return ccdt.Id
						}
					}
					return cdt.Id
				}
			}
			return pdt.Id
		}
	}
	return ""
}

func (p *ChinaRegionService) findRegionItem(regionCode string, items []*RegionItem) *RegionItem {
	for _, item := range items {
		if item.Id == regionCode {
			return item
		}
	}
	return nil
}

func (p *ChinaRegionService) ParseRegionCode(regionCode string) *RegionInfo {
	rinfo := &RegionInfo{}
	//省
	if len(regionCode) >= 2 {
		if item := p.findRegionItem(regionCode[0:2], cn2province); item != nil {
			rinfo.ProvinceId = item.Id
			rinfo.Province = item.Name
		} else {
			return rinfo
		}
	}

	//市
	if len(regionCode) >= 4 {
		if item := p.findRegionItem(regionCode[0:4], p.QueryChinaCities(rinfo.ProvinceId)); item != nil {
			rinfo.CityId = item.Id
			rinfo.City = item.Name
		} else {
			return rinfo
		}
	}

	//区县
	if len(regionCode) >= 6 {
		if item := p.findRegionItem(regionCode[0:6], p.QueryChinaCounties(rinfo.CityId)); item != nil {
			rinfo.CountyId = item.Id
			rinfo.County = item.Name
		} else {
			return rinfo
		}
	}

	//镇
	if len(regionCode) >= 9 {
		if item := p.findRegionItem(regionCode[0:9], p.QueryChinaTowns(rinfo.CountyId)); item != nil {
			rinfo.TownId = item.Id
			rinfo.Town = item.Name
		} else {
			return rinfo
		}
	}

	return rinfo
}

func GetChinaRegionService() *ChinaRegionService {
	return chinaRegionService
}

/*
	加载读入区域数据
*/
func loadChinaRegionData() {

	log.Infof("查询省级数据")

	if !queryProvinceData() {
		return
	}

	log.Infof("查询市级数据")

	province2city = make(map[string][]*RegionItem)
	for _, item := range cn2province {
		provinceId := item.Id
		if ok, data := queryRegionData(provinceId, 2); ok {
			if data != nil {
				province2city[provinceId] = data
			}
		} else {
			return
		}
	}

	log.Infof("查询区级数据")

	city2county = make(map[string][]*RegionItem)
	for _, cities := range province2city {
		for _, item := range cities {
			cityId := item.Id
			if ok, data := queryRegionData(cityId, 3); ok {
				if data != nil {
					city2county[cityId] = data
				}
			} else {
				log.Errorf("city2county error, stop for cityId: %s", cityId)
				return
			}
		}
	}

	county2town = make(map[string][]*RegionItem)
	//暂时不支持镇
	/*
		for _, counties := range city2county {
			for _, item := range counties {
				countyId := item.Id
				if ok, data := queryRegionData(countyId, 4); ok {
					if data != nil {
						county2town[countyId] = data
					}
				} else {
					log.Errorf("county2town error, stop for countyId: %s", countyId)
					return
				}
			}
		}
	*/
}

func queryProvinceData() bool {
	sql := "select region_id, region_name from china_region where level_no = ? and region_id = parent_region_id"
	results, err := db.GetOpenapiMySQLHelper().Query(sql, 1)
	if err != nil {
		log.Warnf("failed to read mysql sql: %s, msg: %s", sql, err.Error())
		return false
	}
	if cn2province == nil && len(results) > 0 {
		cn2province = make([]*RegionItem, 0, len(results))
		for _, dt := range results {
			regionId := string(dt["region_id"].([]byte))
			regionName := string(dt["region_name"].([]byte))
			cn2province = append(cn2province, newRegionItem(regionId, regionName))
		}
	}
	return cn2province != nil
}

func queryRegionData(parentRegionId string, levelNo int) (bool, []*RegionItem) {
	sql := "select region_id, region_name from china_region where level_no = ? and region_id  != parent_region_id and parent_region_id = ?"
	results, err := db.GetOpenapiMySQLHelper().Query(sql, levelNo, parentRegionId)
	if err != nil {
		log.Warnf("failed to read mysql sql: %s, msg: %s", sql, err.Error())
		return false, nil
	}
	if len(results) == 0 {
		return true, nil
	}

	data := make([]*RegionItem, 0, len(results))
	for _, dt := range results {
		regionId := string(dt["region_id"].([]byte))
		regionName := string(dt["region_name"].([]byte))
		data = append(data, newRegionItem(regionId, regionName))
	}
	return true, data
}

func init() {
	chinaRegionService = &ChinaRegionService{}

	cn2province = nil
	province2city = nil
	city2county = nil
	county2town = nil
	t0 := time.Now()
	loadChinaRegionData()
	cost := time.Since(t0)
	log.Infof("loadChinaRegionData cost %s", cost.String())
}
